package client

import (
	"fmt"
	"strconv"
	"time"

	"github.com/goburrow/modbus"
)

// ModbusRTU RTU客户端
type ModbusRTU struct {
	rtuModbusClient  modbus.Client
	rtuClientHandler *modbus.RTUClientHandler

	ComName   string
	ComParity string
	BaudRate  int
	DataBits  int
	StopBits  int
	TimeOut   int
}

// Open 打开
// 参数为一个map,需要包括:
//
//	"modbus.rtu.com_name"   串口地址
//	"modbus.rtu.parity"    	奇偶校验:N O E
//	"modbus.rtu.baud_rate" 	波特率
//	"modbus.rtu.data_bits" 	数据位
//	"modbus.rtu.stop_bits" 	停止位
//	"modbus.time_out"  		超时时间
//	"modbus.slave_id"  		编号
func (rtu *ModbusRTU) Open(config map[string]interface{}) error {

	v, ok := config[address]
	if ok && nil != v {
		rtu.ComName = fmt.Sprintf("%v", v)
	} else {
		return fmt.Errorf("串口地址配置错误,请检查[%v]的配置", address)
	}

	v, ok = config[parity]
	if ok && nil != v {
		rtu.ComParity = fmt.Sprintf("%v", v)
	} else {
		rtu.ComParity = "N"
	}
	v, ok = config[baudRate]
	if ok && nil != v {
		iBaudRate := fmt.Sprintf("%d", v)
		i, e := strconv.Atoi(iBaudRate)
		if nil == e {
			rtu.BaudRate = i
		} else {
			return fmt.Errorf("波特率配置错误,请检查[%v]的配置", baudRate)
		}
	} else {
		return fmt.Errorf("波特率配置错误,请检查[%v]的配置", baudRate)
	}

	v, ok = config[dataBits]
	if ok && nil != v {
		dataBits := fmt.Sprintf("%d", v)
		i, e := strconv.Atoi(dataBits)
		if nil == e {
			rtu.DataBits = i
		} else {
			return fmt.Errorf("数据位配置错误,请检查[%v]的配置", dataBits)
		}
	} else {
		return fmt.Errorf("数据位配置错误,请检查[%v]的配置", dataBits)
	}

	v, ok = config[stopBits]
	if ok && nil != v {
		stopBits := fmt.Sprintf("%d", v)
		i, e := strconv.Atoi(stopBits)
		if nil == e {
			rtu.StopBits = i
		} else {
			return fmt.Errorf("停止位配置错误,请检查[%v]的配置", stopBits)
		}
	} else {
		return fmt.Errorf("停止位配置错误,请检查[%v]的配置", stopBits)
	}

	v, ok = config[timeOut]
	rtu.TimeOut = 500
	if ok && nil != v {
		timeOut := fmt.Sprintf("%d", v)
		i, e := strconv.Atoi(timeOut)
		if nil == e {
			rtu.TimeOut = i
		}
	}
	handler := modbus.NewRTUClientHandler(rtu.ComName)
	handler.BaudRate = rtu.BaudRate
	if rtu.DataBits < 5 || rtu.DataBits > 8 {
		rtu.DataBits = 8
	}

	if rtu.StopBits != 1 && rtu.StopBits != 2 {
		rtu.StopBits = 1
	}
	handler.StopBits = rtu.StopBits
	handler.Address = rtu.ComName
	handler.Parity = parity
	handler.DataBits = rtu.DataBits

	if rtu.ComParity != "N" && rtu.ComParity != "O" && rtu.ComParity != "E" {
		rtu.ComParity = "E"
	}
	handler.Parity = rtu.ComParity

	handler.Timeout = time.Duration(rtu.TimeOut) * time.Millisecond

	v, ok = config[slaveId]
	if !ok {
		return fmt.Errorf("modbus的SlaveID配置错误,请检查地址[%v]的配置", slaveId)
	}
	sid := 254
	if ok && v != nil {
		s := fmt.Sprintf("%d", v)
		sid, _ = strconv.Atoi(s)
	}
	handler.SlaveId = byte(sid)
	err := handler.Connect()
	if err != nil {
		return fmt.Errorf("打开串口失败,错误:%v", err)
	}
	rtu.rtuModbusClient = modbus.NewClient(handler)
	rtu.rtuClientHandler = handler
	return nil
}

// Close 关闭连接
func (rtu *ModbusRTU) Close() {
	if rtu.rtuClientHandler != nil {
		_ = rtu.rtuClientHandler.Close()
	}
	rtu.rtuClientHandler = nil
}

// SetSlaveID set slave id
func (rtu *ModbusRTU) SetSlaveID(slaveID byte) {
	rtu.rtuClientHandler.SlaveId = slaveID
}

// ReadCoils 读1到2000的线圈状态
func (rtu *ModbusRTU) ReadCoils(address, quantity uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadCoils(address, quantity)
}

// ReadDiscreteInputs 读取1到2000的离散输入值
func (rtu *ModbusRTU) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadDiscreteInputs(address, quantity)
}

// WriteSingleCoil 向线圈写单个开关量输入并返回
func (rtu *ModbusRTU) WriteSingleCoil(address, value uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.WriteSingleCoil(address, value)
}

// WriteMultipleCoils forces each coil in a sequence of coils to either
// ON or OFF in a remote device and returns quantity of outputs.
func (rtu *ModbusRTU) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
	return rtu.rtuModbusClient.WriteMultipleCoils(address, quantity, value)
}

// 16-bit access

// ReadInputRegisters reads from 1 to 125 contiguous input registers in
// a remote device and returns input registers.
func (rtu *ModbusRTU) ReadInputRegisters(address, quantity uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadInputRegisters(address, quantity)
}

// ReadHoldingRegisters reads the contents of a contiguous block of
// holding registers in a remote device and returns register value.
func (rtu *ModbusRTU) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadHoldingRegisters(address, quantity)
}

// WriteSingleRegister writes a single holding register in a remote
// device and returns register value.
func (rtu *ModbusRTU) WriteSingleRegister(address, value uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.WriteSingleRegister(address, value)
}

// WriteMultipleRegisters writes a block of contiguous registers
// (1 to 123 registers) in a remote device and returns quantity of
// registers.
func (rtu *ModbusRTU) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) {
	return rtu.rtuModbusClient.WriteMultipleRegisters(address, quantity, value)
}

// ReadWriteMultipleRegisters performs a combination of one read
// operation and one write operation. It returns read registers value.
func (rtu *ModbusRTU) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity, value)
}

// MaskWriteRegister modify the contents of a specified holding
// register using a combination of an AND mask, an OR mask, and the
// register limiter current contents. The function returns
// AND-mask and OR-mask.
func (rtu *ModbusRTU) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.MaskWriteRegister(address, andMask, orMask)
}

// ReadFIFOQueue reads the contents of a First-In-First-Out (FIFO) queue
// of register in a remote device and returns FIFO value register.
func (rtu *ModbusRTU) ReadFIFOQueue(address uint16) (results []byte, err error) {
	return rtu.rtuModbusClient.ReadFIFOQueue(address)
}
